From 3e09f70259621792dd1b7e1171698e581794ca66 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Thu, 19 Jun 2014 00:55:17 -0700 Subject: [PATCH] Initial pass at boxed errors The next step is to clean up the error handling in general so that failure cases produce good errors. --- src/bin/cargo-compile.rs | 12 +- src/bin/cargo-git-checkout.rs | 17 +-- src/bin/cargo-read-manifest.rs | 9 +- src/bin/cargo.rs | 35 +++-- src/cargo/core/manifest.rs | 4 +- src/cargo/core/resolver.rs | 3 +- src/cargo/core/version_req.rs | 14 +- src/cargo/lib.rs | 31 ++-- src/cargo/ops/cargo_compile.rs | 4 +- src/cargo/ops/cargo_read_manifest.rs | 8 +- src/cargo/ops/cargo_rustc.rs | 39 ++--- src/cargo/sources/git/utils.rs | 15 +- src/cargo/sources/path.rs | 4 +- src/cargo/util/config.rs | 32 ++-- src/cargo/util/errors.rs | 203 ++++++++++++++++++++++++ src/cargo/util/important_paths.rs | 8 +- src/cargo/util/mod.rs | 5 +- src/cargo/util/process_builder.rs | 20 +-- src/cargo/util/result.rs | 221 +-------------------------- src/cargo/util/toml.rs | 17 +-- tests/support/mod.rs | 4 +- tests/test_cargo_compile.rs | 4 +- tests/test_cargo_compile_git_deps.rs | 19 ++- 23 files changed, 363 insertions(+), 365 deletions(-) create mode 100644 src/cargo/util/errors.rs diff --git a/src/bin/cargo-compile.rs b/src/bin/cargo-compile.rs index 1c0220383..e327d7e79 100644 --- a/src/bin/cargo-compile.rs +++ b/src/bin/cargo-compile.rs @@ -10,10 +10,10 @@ extern crate log; use std::os; use hammer::FlagConfig; -use cargo::{execute_main_without_stdin,CLIResult,CLIError,ToResult}; +use cargo::{execute_main_without_stdin}; use cargo::ops; +use cargo::util::{CliResult, CliError}; use cargo::util::important_paths::find_project; -use cargo::util::{ToCLI}; #[deriving(PartialEq,Clone,Decodable,Encodable)] pub struct Options { @@ -26,16 +26,16 @@ fn main() { execute_main_without_stdin(execute); } -fn execute(options: Options) -> CLIResult> { +fn execute(options: Options) -> CliResult> { debug!("executing; cmd=cargo-compile; args={}", os::args()); let root = match options.manifest_path { Some(path) => Path::new(path), None => try!(find_project(os::getcwd(), "Cargo.toml") .map(|path| path.join("Cargo.toml")) - .to_result(|err| - CLIError::new("Could not find Cargo.toml in this directory or any parent directory", Some(err), 102))) + .map_err(|_| + CliError::new("Could not find Cargo.toml in this directory or any parent directory", 102))) }; - ops::compile(&root).map(|_| None).to_cli(101) + ops::compile(&root).map(|_| None).map_err(|err| CliError::from_boxed(err, 101)) } diff --git a/src/bin/cargo-git-checkout.rs b/src/bin/cargo-git-checkout.rs index 64ddc176d..6f3f37425 100644 --- a/src/bin/cargo-git-checkout.rs +++ b/src/bin/cargo-git-checkout.rs @@ -6,10 +6,10 @@ extern crate hammer; extern crate url; use hammer::FlagConfig; -use cargo::{execute_main_without_stdin,CLIResult,CLIError,ToResult}; +use cargo::{execute_main_without_stdin}; use cargo::core::source::{Source,SourceId}; use cargo::sources::git::{GitSource}; -use cargo::util::{Config,ToCLI}; +use cargo::util::{Config, CliResult, CliError, Require}; use url::Url; #[deriving(PartialEq,Clone,Decodable)] @@ -25,19 +25,18 @@ fn main() { execute_main_without_stdin(execute); } -fn execute(options: Options) -> CLIResult> { +fn execute(options: Options) -> CliResult> { let Options { url, reference, .. } = options; - let url: Url = try!(from_str(url.as_slice()).to_result(|_| - CLIError::new(format!("The URL `{}` you passed was not a valid URL", url), None::<&str>, 1))); + let url: Url = try!(from_str(url.as_slice()) + .require(|| format!("The URL `{}` you passed was not a valid URL", url)) + .map_err(|e| CliError::from_boxed(e, 1))); let source_id = SourceId::for_git(&url, reference.as_slice()); - let mut source = GitSource::new(&source_id, &try!(Config::new().to_cli(1))); + let mut source = GitSource::new(&source_id, &try!(Config::new().map_err(|e| CliError::from_boxed(e, 1)))); - try!(source.update().map_err(|e| { - CLIError::new(format!("Couldn't update {}: {}", source, e), None::<&str>, 1) - })); + try!(source.update().map_err(|e| CliError::new(format!("Couldn't update {}: {}", source, e), 1))); Ok(None) } diff --git a/src/bin/cargo-read-manifest.rs b/src/bin/cargo-read-manifest.rs index 4d97627ce..225c811d9 100644 --- a/src/bin/cargo-read-manifest.rs +++ b/src/bin/cargo-read-manifest.rs @@ -5,8 +5,9 @@ extern crate serialize; extern crate hammer; use hammer::FlagConfig; -use cargo::{execute_main_without_stdin,CLIResult,CLIError}; -use cargo::core::{Package,Source,SourceId}; +use cargo::{execute_main_without_stdin}; +use cargo::core::{Package, Source, SourceId}; +use cargo::util::{CliResult, CliError}; use cargo::sources::{PathSource}; #[deriving(PartialEq,Clone,Decodable)] @@ -20,7 +21,7 @@ fn main() { execute_main_without_stdin(execute); } -fn execute(options: Options) -> CLIResult> { +fn execute(options: Options) -> CliResult> { let source_id = SourceId::for_path(&Path::new(options.manifest_path.as_slice())); let mut source = PathSource::new(&source_id); @@ -29,5 +30,5 @@ fn execute(options: Options) -> CLIResult> { source .get_root_package() .map(|pkg| Some(pkg)) - .map_err(|err| CLIError::new(err.get_desc(), Some(err.get_detail()), 1)) + .map_err(|err| CliError::from_boxed(err, 1)) } diff --git a/src/bin/cargo.rs b/src/bin/cargo.rs index 5293000e4..2daaf3bef 100644 --- a/src/bin/cargo.rs +++ b/src/bin/cargo.rs @@ -12,9 +12,8 @@ use std::os; use std::io::process::{Command,InheritFd,ExitStatus,ExitSignal}; use serialize::Encodable; use cargo::{NoFlags,execute_main_without_stdin,handle_error}; -use cargo::core::errors::{CLIError,CLIResult,ToResult}; use cargo::util::important_paths::find_project; -use cargo::util::{ToCLI,config,simple_human}; +use cargo::util::{CliError, CliResult, CargoResult, CargoError, Require, config, box_error}; fn main() { execute(); @@ -58,17 +57,17 @@ fn execute() { .stderr(InheritFd(2)) .status(); - match command.map_err(|_| simple_human("No such subcommand")) { + match command { Ok(ExitStatus(0)) => (), - Ok(ExitStatus(i)) | Ok(ExitSignal(i)) => handle_error(simple_human("").to_cli(i as uint)), - Err(err) => handle_error(err.to_cli(127)) + Ok(ExitStatus(i)) | Ok(ExitSignal(i)) => handle_error(CliError::new("", i as uint)), + Err(_) => handle_error(CliError::new("No such subcommand", 127)) } } } -fn process(args: Vec) -> CLIResult<(String, Vec)> { +fn process(args: Vec) -> CliResult<(String, Vec)> { let args: Vec = Vec::from_slice(args.tail()); - let head = try!(args.iter().nth(0).to_result(|_| CLIError::new("No subcommand found", None::<&str>, 1))).to_str(); + let head = try!(args.iter().nth(0).require(|| "No subcommand found").map_err(|err| CliError::from_boxed(err, 1))).to_str(); let tail = Vec::from_slice(args.tail()); Ok((head, tail)) @@ -91,9 +90,9 @@ impl FlagConfig for ConfigForKeyFlags { } } -fn config_for_key(args: ConfigForKeyFlags) -> CLIResult> { - let value = try!(config::get_config(os::getcwd(), args.key.as_slice()).to_result(|err| - CLIError::new("Couldn't load configuration", Some(err), 1))); +fn config_for_key(args: ConfigForKeyFlags) -> CliResult> { + let value = try!(config::get_config(os::getcwd(), args.key.as_slice()).map_err(|err| + CliError::new("Couldn't load configuration", 1))); if args.human { println!("{}", value); @@ -116,9 +115,9 @@ impl FlagConfig for ConfigListFlags { } } -fn config_list(args: ConfigListFlags) -> CLIResult> { - let configs = try!(config::all_configs(os::getcwd()).to_result(|err| - CLIError::new("Couldn't load conifguration", Some(err), 1))); +fn config_list(args: ConfigListFlags) -> CliResult> { + let configs = try!(config::all_configs(os::getcwd()).map_err(|e| + CliError::new("Couldn't load configuration", 1))); if args.human { for (key, value) in configs.iter() { @@ -130,12 +129,12 @@ fn config_list(args: ConfigListFlags) -> CLIResult> { } } -fn locate_project(_: NoFlags) -> CLIResult> { - let root = try!(find_project(os::getcwd(), "Cargo.toml").to_result(|err| - CLIError::new(err.to_str(), None::<&str>, 1))); +fn locate_project(_: NoFlags) -> CliResult> { + let root = try!(find_project(os::getcwd(), "Cargo.toml").map_err(|e| CliError::from_boxed(e, 1))); - let string = try!(root.as_str().to_result(|_| - CLIError::new(format!("Your project path contains characters not representable in Unicode: {}", os::getcwd().display()), None::<&str>, 1))); + let string = try!(root.as_str() + .require(|| "Your project path contains characters not representable in Unicode") + .map_err(|e| CliError::from_boxed(e, 1))); Ok(Some(ProjectLocation { root: string.to_str() })) } diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index b8d8b48ec..da904e174 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -10,7 +10,7 @@ use core::{ Summary }; use core::dependency::SerializedDependency; -use util::{CargoResult,simple_human}; +use util::{CargoResult, CargoError, box_error}; #[deriving(PartialEq,Clone)] pub struct Manifest { @@ -65,7 +65,7 @@ impl LibKind { "rlib" => Ok(Rlib), "dylib" => Ok(Dylib), "staticlib" => Ok(StaticLib), - _ => Err(simple_human(format!("{} was not one of lib|rlib|dylib|staticlib", string))) + _ => Err(box_error(format!("{} was not one of lib|rlib|dylib|staticlib", string))) } } diff --git a/src/cargo/core/resolver.rs b/src/cargo/core/resolver.rs index 23eac0b6b..3c6bcf93b 100644 --- a/src/cargo/core/resolver.rs +++ b/src/cargo/core/resolver.rs @@ -1,11 +1,12 @@ use std::collections::HashMap; + use core::{ Dependency, PackageId, Summary, Registry }; -use util::result::CargoResult; +use util::errors::CargoResult; /* TODO: * - The correct input here is not a registry. Resolves should be performable diff --git a/src/cargo/core/version_req.rs b/src/cargo/core/version_req.rs index 1b4d7bcf7..065f26bbe 100644 --- a/src/cargo/core/version_req.rs +++ b/src/cargo/core/version_req.rs @@ -1,7 +1,7 @@ use std::fmt; use std::str::CharOffsets; use semver::Version; -use util::{other_error,CargoResult}; +use util::{CargoResult, error}; #[deriving(PartialEq,Clone)] pub struct VersionReq { @@ -49,7 +49,7 @@ impl VersionReq { } if lexer.is_error() { - return Err(other_error("invalid version requirement")); + return Err(error("invalid version requirement")); } predicates.push(try!(builder.build())); @@ -151,12 +151,12 @@ impl PredBuilder { fn set_sigil(&mut self, sigil: &str) -> CargoResult<()> { if self.op.is_some() { - return Err(other_error("op already set")); + return Err(error("op already set")); } match Op::from_sigil(sigil) { Some(op) => self.op = Some(op), - _ => return Err(other_error("invalid sigil")) + _ => return Err(error("invalid sigil")) } Ok(()) @@ -188,12 +188,12 @@ impl PredBuilder { fn build(&self) -> CargoResult { let op = match self.op { Some(x) => x, - None => return Err(other_error("op required")) + None => return Err(error("op required")) }; let major = match self.major { Some(x) => x, - None => return Err(other_error("major version required")) + None => return Err(error("major version required")) }; Ok(Predicate { @@ -384,7 +384,7 @@ fn parse_version_part(s: &str) -> CargoResult { let n = (c as uint) - ('0' as uint); if n > 9 { - return Err(other_error("version components must be numeric")); + return Err(error("version components must be numeric")); } ret *= 10; diff --git a/src/cargo/lib.rs b/src/cargo/lib.rs index ed65a4583..c8dd26ef2 100644 --- a/src/cargo/lib.rs +++ b/src/cargo/lib.rs @@ -20,7 +20,7 @@ extern crate hamcrest; use serialize::{Decoder,Encoder,Decodable,Encodable,json}; use std::io; use hammer::{FlagDecoder,FlagConfig,HammerError}; -pub use core::errors::{CLIError,CLIResult,ToResult}; +pub use util::{CliError, CliResult}; macro_rules! some( ($e:expr) => ( @@ -46,8 +46,8 @@ pub struct NoFlags; impl FlagConfig for NoFlags {} -pub fn execute_main<'a, T: RepresentsFlags, U: RepresentsJSON, V: Encodable, io::IoError>>(exec: fn(T, U) -> CLIResult>) { - fn call<'a, T: RepresentsFlags, U: RepresentsJSON, V: Encodable, io::IoError>>(exec: fn(T, U) -> CLIResult>) -> CLIResult> { +pub fn execute_main<'a, T: RepresentsFlags, U: RepresentsJSON, V: Encodable, io::IoError>>(exec: fn(T, U) -> CliResult>) { + fn call<'a, T: RepresentsFlags, U: RepresentsJSON, V: Encodable, io::IoError>>(exec: fn(T, U) -> CliResult>) -> CliResult> { let flags = try!(flags_from_args::()); let json = try!(json_from_stdin::()); @@ -57,8 +57,8 @@ pub fn execute_main<'a, T: RepresentsFlags, U: RepresentsJSON, V: Encodable, io::IoError>>(exec: fn(T) -> CLIResult>) { - fn call<'a, T: RepresentsFlags, V: Encodable, io::IoError>>(exec: fn(T) -> CLIResult>) -> CLIResult> { +pub fn execute_main_without_stdin<'a, T: RepresentsFlags, V: Encodable, io::IoError>>(exec: fn(T) -> CliResult>) { + fn call<'a, T: RepresentsFlags, V: Encodable, io::IoError>>(exec: fn(T) -> CliResult>) -> CliResult> { let flags = try!(flags_from_args::()); exec(flags) @@ -67,7 +67,7 @@ pub fn execute_main_without_stdin<'a, T: RepresentsFlags, V: Encodable, io::IoError>>(result: CLIResult>) { +pub fn process_executed<'a, T: Encodable, io::IoError>>(result: CliResult>) { match result { Err(e) => handle_error(e), Ok(encodable) => { @@ -79,11 +79,12 @@ pub fn process_executed<'a, T: Encodable, io::IoError>>(result } } -pub fn handle_error(err: CLIError) { +pub fn handle_error(err: CliError) { log!(4, "handle_error; err={}", err); - let CLIError { msg, exit_code, .. } = err; - let _ = write!(&mut std::io::stderr(), "{}", msg); + let CliError { error, exit_code, .. } = err; + let _ = write!(&mut std::io::stderr(), "{}", error); + // TODO: Cause chains //detail.map(|d| write!(&mut std::io::stderr(), ":\n{}", d)); std::os::set_exit_status(exit_code as int); @@ -93,17 +94,17 @@ fn args() -> Vec { std::os::args() } -fn flags_from_args() -> CLIResult { +fn flags_from_args() -> CliResult { let mut decoder = FlagDecoder::new::(args().tail()); - Decodable::decode(&mut decoder).to_result(|e: HammerError| CLIError::new(e.message, None::<&str>, 1)) + Decodable::decode(&mut decoder).map_err(|e: HammerError| CliError::new(e.message, 1)) } -fn json_from_stdin() -> CLIResult { +fn json_from_stdin() -> CliResult { let mut reader = io::stdin(); - let input = try!(reader.read_to_str().to_result(|_| CLIError::new("Standard in did not exist or was not UTF-8", None::<&str>, 1))); + let input = try!(reader.read_to_str().map_err(|e| CliError::new("Standard in did not exist or was not UTF-8", 1))); - let json = try!(json::from_str(input.as_slice()).to_result(|_| CLIError::new("Could not parse standard in as JSON", Some(input.clone()), 1))); + let json = try!(json::from_str(input.as_slice()).map_err(|e| CliError::new("Could not parse standard in as JSON", 1))); let mut decoder = json::Decoder::new(json); - Decodable::decode(&mut decoder).to_result(|e: json::DecoderError| CLIError::new("Could not process standard in as input", Some(e), 1)) + Decodable::decode(&mut decoder).map_err(|e: json::DecoderError| CliError::new("Could not process standard in as input", 1)) } diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index dafa492bd..1ca4e2eee 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -20,7 +20,7 @@ use core::{Source,SourceId,PackageSet,resolver}; use core::registry::PackageRegistry; use ops; use sources::{PathSource}; -use util::{CargoResult,Wrap,config,other_error}; +use util::{CargoResult, Wrap, config, error}; pub fn compile(manifest_path: &Path) -> CargoResult<()> { log!(4, "compile; manifest-path={}", manifest_path.display()); @@ -56,7 +56,7 @@ fn source_ids_from_config() -> CargoResult> { let config_paths = configs.find_equiv(&"paths").map(|v| v.clone()).unwrap_or_else(|| ConfigValue::new()); let paths: Vec = match config_paths.get_value() { - &config::String(_) => return Err(other_error("The path was configured as a String instead of a List")), + &config::String(_) => return Err(error("The path was configured as a String instead of a List")), &config::List(ref list) => list.iter().map(|path| Path::new(path.as_slice())).collect() }; diff --git a/src/cargo/ops/cargo_read_manifest.rs b/src/cargo/ops/cargo_read_manifest.rs index c1f723bd3..809c38a20 100644 --- a/src/cargo/ops/cargo_read_manifest.rs +++ b/src/cargo/ops/cargo_read_manifest.rs @@ -1,16 +1,16 @@ use std::io::File; use util; use core::{Package,Manifest,SourceId}; -use util::{CargoResult,io_error}; +use util::{CargoResult, CargoError, box_error, human}; pub fn read_manifest(contents: &[u8], source_id: &SourceId) -> CargoResult<(Manifest, Vec)> { - util::toml::to_manifest(contents, source_id) + util::toml::to_manifest(contents, source_id).map_err(human) } pub fn read_package(path: &Path, source_id: &SourceId) -> CargoResult<(Package, Vec)> { log!(5, "read_package; path={}; source-id={}", path.display(), source_id); - let mut file = try!(File::open(path).map_err(io_error)); - let data = try!(file.read_to_end().map_err(io_error)); + let mut file = try!(File::open(path).map_err(box_error)); + let data = try!(file.read_to_end().map_err(box_error)); let (manifest, nested) = try!(read_manifest(data.as_slice(), source_id)); Ok((Package::new(manifest, path), nested)) diff --git a/src/cargo/ops/cargo_rustc.rs b/src/cargo/ops/cargo_rustc.rs index 2d38610c5..90cb85f8a 100644 --- a/src/cargo/ops/cargo_rustc.rs +++ b/src/cargo/ops/cargo_rustc.rs @@ -4,8 +4,7 @@ use std::path::Path; use std::str; use core::{Package,PackageSet,Target}; use util; -use util::{other_error,human_error,CargoResult,CargoError,ProcessBuilder}; -use util::result::ProcessError; +use util::{CargoResult, CargoError, ProcessBuilder, error, human, box_error}; type Args = Vec; @@ -48,7 +47,7 @@ fn compile_pkg(pkg: &Package, dest: &Path, deps_dir: &Path, primary: bool) -> Ca fn mk_target(target: &Path) -> CargoResult<()> { io::fs::mkdir_recursive(target, io::UserRWX) - .map_err(|_| other_error("could not create target directory")) + .map_err(|_| error("could not create target directory")) } fn rustc(root: &Path, target: &Target, dest: &Path, deps: &Path, verbose: bool) -> CargoResult<()> { @@ -62,10 +61,16 @@ fn rustc(root: &Path, target: &Target, dest: &Path, deps: &Path, verbose: bool) let rustc = prepare_rustc(root, target, *crate_type, dest, deps); try!((if verbose { - rustc.exec() + rustc.exec().map_err(|err| { + log!(5, "exec failed; error={}", err.description()); + human(err) + }) } else { - rustc.exec_with_output().and(Ok(())) - }).map_err(|e| rustc_to_cargo_err(rustc.get_args().as_slice(), root, e))); + rustc.exec_with_output().and(Ok(())).map_err(|err| { + log!(5, "exec_with_output failed; error={}", err.description()); + human(err) + }) + })); } Ok(()) @@ -97,29 +102,9 @@ fn build_deps_args(dst: &mut Args, deps: &Path) { dst.push(deps.display().to_str()); } -fn rustc_to_cargo_err(args: &[String], cwd: &Path, err: CargoError) -> CargoError { - let msg = { - let output = match err { - CargoError { kind: ProcessError(_, ref output), .. } => output, - _ => fail!("Bug! exec() returned an error other than a ProcessError") - }; - - let mut msg = format!("failed to execute: `rustc {}`", args.connect(" ")); - - output.as_ref().map(|o| { - let second = format!("; Error:\n{}", str::from_utf8_lossy(o.error.as_slice())); - msg.push_str(second.as_slice()); - }); - - msg - }; - - human_error(msg, format!("root={}", cwd.display()), err) -} - fn topsort(deps: &PackageSet) -> CargoResult { match deps.sort() { Some(deps) => Ok(deps), - None => return Err(other_error("circular dependency detected")) + None => return Err(error("circular dependency detected")) } } diff --git a/src/cargo/sources/git/utils.rs b/src/cargo/sources/git/utils.rs index 8cccdb584..f8f9f5954 100644 --- a/src/cargo/sources/git/utils.rs +++ b/src/cargo/sources/git/utils.rs @@ -1,5 +1,5 @@ use url::Url; -use util::{CargoResult,ProcessBuilder,io_error,human_error,process}; +use util::{CargoResult, ProcessBuilder, process, box_error}; use std::fmt; use std::fmt::{Show,Formatter}; use std::str; @@ -173,8 +173,7 @@ impl GitRemote { fn clone_into(&self, path: &Path) -> CargoResult<()> { let dirname = Path::new(path.dirname()); - try!(mkdir_recursive(path, UserDir).map_err(|err| - human_error(format!("Couldn't recursively create `{}`", dirname.display()), format!("path={}", dirname.display()), io_error(err)))); + try!(mkdir_recursive(path, UserDir).map_err(box_error)); Ok(git!(dirname, "clone {} {} --bare --no-hardlinks --quiet", self.fetch_location(), path.display())) } @@ -228,15 +227,15 @@ impl GitCheckout { let dirname = Path::new(self.location.dirname()); try!(mkdir_recursive(&dirname, UserDir).map_err(|e| - human_error(format!("Couldn't mkdir {}", Path::new(self.location.dirname()).display()), None::<&str>, io_error(e)))); + box_error(format!("Couldn't mkdir {}", Path::new(self.location.dirname()).display())))); if self.location.exists() { try!(rmdir_recursive(&self.location).map_err(|e| - human_error(format!("Couldn't rmdir {}", Path::new(&self.location).display()), None::<&str>, io_error(e)))); + box_error(format!("Couldn't rmdir {}", Path::new(&self.location).display())))); } git!(dirname, "clone --no-checkout --quiet {} {}", self.get_source().display(), self.location.display()); - try!(chmod(&self.location, AllPermissions).map_err(io_error)); + try!(chmod(&self.location, AllPermissions).map_err(box_error)); Ok(()) } @@ -264,12 +263,12 @@ fn git(path: &Path, str: &str) -> ProcessBuilder { fn git_inherit(path: &Path, str: String) -> CargoResult<()> { git(path, str.as_slice()).exec().map_err(|err| - human_error(format!("Executing `git {}` failed: {}", str, err), None::<&str>, err)) + box_error(format!("Executing `git {}` failed: {}", str, err))) } fn git_output(path: &Path, str: String) -> CargoResult { let output = try!(git(path, str.as_slice()).exec_with_output().map_err(|err| - human_error(format!("Executing `git {}` failed", str), None::<&str>, err))); + box_error(format!("Executing `git {}` failed", str)))); Ok(to_str(output.output.as_slice()).as_slice().trim_right().to_str()) } diff --git a/src/cargo/sources/path.rs b/src/cargo/sources/path.rs index c8e9d6596..918027469 100644 --- a/src/cargo/sources/path.rs +++ b/src/cargo/sources/path.rs @@ -2,7 +2,7 @@ use std::fmt; use std::fmt::{Show,Formatter}; use core::{Package,PackageId,Summary,SourceId,Source}; use ops; -use util::{CargoResult,simple_human}; +use util::{CargoResult, box_error}; pub struct PathSource { id: SourceId, @@ -45,7 +45,7 @@ impl PathSource { match self.packages.as_slice().head() { Some(pkg) => Ok(pkg.clone()), - None => Err(simple_human("no package found in source")) + None => Err(box_error("no package found in source")) } } } diff --git a/src/cargo/util/config.rs b/src/cargo/util/config.rs index ab1126ea2..535b87fe7 100644 --- a/src/cargo/util/config.rs +++ b/src/cargo/util/config.rs @@ -2,7 +2,7 @@ use std::{io,fmt,os}; use std::collections::HashMap; use serialize::{Encodable,Encoder}; use toml; -use util::{CargoResult,Require,other_error,simple_human}; +use util::{CargoResult, Require, error, internal_error}; pub struct Config { home_path: Path @@ -12,7 +12,7 @@ impl Config { pub fn new() -> CargoResult { Ok(Config { home_path: try!(os::homedir() - .require(simple_human("Couldn't find the home directory"))) + .require(|| "Couldn't find the home directory")) }) } @@ -96,7 +96,7 @@ impl fmt::Show for ConfigValue { pub fn get_config(pwd: Path, key: &str) -> CargoResult { find_in_tree(&pwd, |file| extract_config(file, key)) - .map_err(|_| other_error("config key not found").with_detail(format!("key={}", key))) + .map_err(|_| internal_error("config key not found", format!("key={}", key))) } pub fn all_configs(pwd: Path) -> CargoResult> { @@ -115,7 +115,7 @@ fn find_in_tree(pwd: &Path, walk: |io::fs::File| -> CargoResult) -> CargoR loop { let possible = current.join(".cargo").join("config"); if possible.exists() { - let file = try!(io::fs::File::open(&possible).map_err(|_| other_error("could not open file"))); + let file = try!(io::fs::File::open(&possible).map_err(|_| error("could not open file"))); match walk(file) { Ok(res) => return Ok(res), _ => () @@ -125,7 +125,7 @@ fn find_in_tree(pwd: &Path, walk: |io::fs::File| -> CargoResult) -> CargoR if !current.pop() { break; } } - Err(other_error("")) + Err(error("")) } fn walk_tree(pwd: &Path, walk: |io::fs::File| -> CargoResult<()>) -> CargoResult<()> { @@ -135,14 +135,14 @@ fn walk_tree(pwd: &Path, walk: |io::fs::File| -> CargoResult<()>) -> CargoResult loop { let possible = current.join(".cargo").join("config"); if possible.exists() { - let file = try!(io::fs::File::open(&possible).map_err(|_| other_error("could not open file"))); + let file = try!(io::fs::File::open(&possible).map_err(|_| error("could not open file"))); match walk(file) { Err(_) => err = false, _ => () } } - if err { return Err(other_error("")); } + if err { return Err(error("")); } if !current.pop() { break; } } @@ -152,13 +152,13 @@ fn walk_tree(pwd: &Path, walk: |io::fs::File| -> CargoResult<()>) -> CargoResult fn extract_config(file: io::fs::File, key: &str) -> CargoResult { let path = file.path().clone(); let mut buf = io::BufferedReader::new(file); - let root = try!(toml::parse_from_buffer(&mut buf).map_err(|_| other_error(""))); - let val = try!(root.lookup(key).require(other_error(""))); + let root = try!(toml::parse_from_buffer(&mut buf).map_err(|_| error(""))); + let val = try!(root.lookup(key).require(|| error(""))); let v = match val { &toml::String(ref val) => String(val.clone()), &toml::Array(ref val) => List(val.iter().map(|s: &toml::Value| s.to_str()).collect()), - _ => return Err(other_error("")) + _ => return Err(error("")) }; Ok(ConfigValue{ value: v, path: vec!(path) }) @@ -168,10 +168,10 @@ fn extract_all_configs(file: io::fs::File, map: &mut HashMap () } @@ -193,11 +193,11 @@ fn extract_all_configs(file: io::fs::File, map: &mut HashMap CargoResult<()> { match existing.value { - String(_) => return Err(other_error("should be an Array, but it was a String")), + String(_) => return Err(error("should be an Array, but it was a String")), List(ref mut list) => { let new_list: Vec> = val.iter().map(|s: &toml::Value| toml_string(s)).collect(); if new_list.iter().any(|v| v.is_err()) { - return Err(other_error("should be an Array of Strings, but was an Array of other values")); + return Err(error("should be an Array of Strings, but was an Array of other values")); } else { let new_list: Vec = new_list.move_iter().map(|v| v.unwrap()).collect(); list.push_all(new_list.as_slice()); @@ -211,6 +211,6 @@ fn merge_array(existing: &mut ConfigValue, val: &[toml::Value], path: &Path) -> fn toml_string(val: &toml::Value) -> CargoResult { match val { &toml::String(ref str) => Ok(str.clone()), - _ => Err(other_error("")) + _ => Err(error("")) } } diff --git a/src/cargo/util/errors.rs b/src/cargo/util/errors.rs new file mode 100644 index 000000000..833cd014a --- /dev/null +++ b/src/cargo/util/errors.rs @@ -0,0 +1,203 @@ +use std::io::process::{Command,ProcessOutput,ProcessExit,ExitStatus,ExitSignal}; +use std::io::IoError; +use std::fmt; +use std::fmt::{Show, Formatter}; + +use TomlError = toml::Error; + +pub trait CargoError { + fn description(&self) -> String; + fn detail(&self) -> Option { None } + fn cause<'a>(&'a self) -> Option<&'a CargoError> { None } + fn is_human(&self) -> bool { false } + + fn concrete(&self) -> ConcreteCargoError { + ConcreteCargoError { + description: self.description(), + detail: self.detail(), + cause: self.cause().map(|c| box c.concrete() as Box), + is_human: self.is_human() + } + } +} + +impl Show for Box { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + try!(write!(f, "{}", self.description())); + Ok(()) + } +} + +impl CargoError for Box { + fn description(&self) -> String { + (*self).description() + } + + fn detail(&self) -> Option { + (*self).detail() + } + + fn cause<'a>(&'a self) -> Option<&'a CargoError> { + (*self).cause() + } + + fn is_human(&self) -> bool { + (*self).is_human() + } +} + +pub type CargoResult = Result>; + +impl CargoError for &'static str { + fn description(&self) -> String { self.to_str() } + fn is_human(&self) -> bool { true } +} + +impl CargoError for String { + fn description(&self) -> String { self.to_str() } + fn is_human(&self) -> bool { true } +} + +impl CargoError for IoError { + fn description(&self) -> String { self.to_str() } +} + +impl CargoError for TomlError { + fn description(&self) -> String { self.to_str() } +} + +pub struct ProcessError { + pub command: String, + pub exit: Option, + pub output: Option, + pub detail: Option, + pub cause: Option> +} + +impl Show for ProcessError { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let exit = match self.exit { + Some(ExitStatus(i)) | Some(ExitSignal(i)) => i.to_str(), + None => "never executed".to_str() + }; + write!(f, "process failed: `{}` (status={})", self.command, exit) + } +} + +impl CargoError for ProcessError { + fn description(&self) -> String { + let exit = match self.exit { + Some(ExitStatus(i)) | Some(ExitSignal(i)) => i.to_str(), + None => "never executed".to_str() + }; + format!("Executing `{}` failed (status={})", self.command, exit) + } + + fn detail(&self) -> Option { + self.detail.clone() + } + + fn cause<'a>(&'a self) -> Option<&'a CargoError> { + self.cause.as_ref().map(|c| { let err: &CargoError = *c; err }) + } +} + +struct ConcreteCargoError { + description: String, + detail: Option, + cause: Option>, + is_human: bool +} + +impl Show for ConcreteCargoError { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}", self.description) + } +} + +impl CargoError for ConcreteCargoError { + fn description(&self) -> String { + self.description.clone() + } + + fn detail(&self) -> Option { + self.detail.clone() + } + + fn cause<'a>(&'a self) -> Option<&'a CargoError> { + self.cause.as_ref().map(|c| { let err: &CargoError = *c; err }) + } + + fn is_human(&self) -> bool { + self.is_human + } +} + +pub type CliResult = Result; + +#[deriving(Show)] +pub struct CliError { + pub error: Box, + pub exit_code: uint +} + +impl CliError { + pub fn new(error: E, code: uint) -> CliError { + let error = box error as Box; + CliError::from_boxed(error, code) + } + + pub fn from_boxed(error: Box, code: uint) -> CliError { + let error = if error.is_human() { + error + } else { + chain(error, "An unknown error occurred") + }; + + CliError { error: error, exit_code: code } + } +} + +pub fn process_error(msg: S, command: &Command, status: Option<&ProcessExit>, output: Option<&ProcessOutput>) -> ProcessError { + ProcessError { + command: command.to_str(), + exit: status.map(|o| o.clone()), + output: output.map(|o| o.clone()), + detail: None, + cause: None + } +} + +pub fn internal_error(error: S1, detail: S2) -> Box { + box ConcreteCargoError { + description: error.as_slice().to_str(), + detail: Some(detail.as_slice().to_str()), + cause: None, + is_human: false + } as Box +} + +pub fn error(error: S1) -> Box { + box ConcreteCargoError { + description: error.as_slice().to_str(), + detail: None, + cause: None, + is_human: false + } as Box +} + +pub fn human(error: E) -> Box { + let mut concrete = error.concrete(); + concrete.is_human = true; + box concrete as Box +} + +pub fn chain(original: Box, update: E) -> Box { + let mut concrete = update.concrete(); + concrete.cause = Some(original); + box concrete as Box +} + +pub fn box_error(err: S) -> Box { + box err as Box +} diff --git a/src/cargo/util/important_paths.rs b/src/cargo/util/important_paths.rs index 8238309dc..f8fd25981 100644 --- a/src/cargo/util/important_paths.rs +++ b/src/cargo/util/important_paths.rs @@ -1,4 +1,4 @@ -use util::{other_error,CargoResult,CargoError}; +use util::{CargoResult, CargoError, internal_error}; pub fn find_project(pwd: Path, file: &str) -> CargoResult { let mut current = pwd.clone(); @@ -14,7 +14,7 @@ pub fn find_project(pwd: Path, file: &str) -> CargoResult { Err(manifest_missing_err(&pwd, file.as_slice())) } -fn manifest_missing_err(pwd: &Path, file: &str) -> CargoError { - other_error("manifest not found") - .with_detail(format!("pwd={}; file={}", pwd.display(), file)) +fn manifest_missing_err(pwd: &Path, file: &str) -> Box { + internal_error("manifest not found", + format!("pwd={}; file={}", pwd.display(), file)) } diff --git a/src/cargo/util/mod.rs b/src/cargo/util/mod.rs index 848d94cc9..8957948c9 100644 --- a/src/cargo/util/mod.rs +++ b/src/cargo/util/mod.rs @@ -1,6 +1,8 @@ pub use self::config::Config; pub use self::process_builder::{process,ProcessBuilder}; -pub use self::result::{CargoError,CargoResult,Wrap,Require,ToCLI,other_error,human_error,simple_human,toml_error,io_error,process_error}; +pub use self::result::{Wrap, Require}; +pub use self::errors::{CargoResult, CargoError, CliResult, CliError, ProcessError}; +pub use self::errors::{process_error, box_error, internal_error, error, human, chain}; pub use self::paths::realpath; pub mod graph; @@ -10,3 +12,4 @@ pub mod important_paths; pub mod result; pub mod toml; pub mod paths; +pub mod errors; diff --git a/src/cargo/util/process_builder.rs b/src/cargo/util/process_builder.rs index 6d26eea26..6adede8eb 100644 --- a/src/cargo/util/process_builder.rs +++ b/src/cargo/util/process_builder.rs @@ -3,7 +3,7 @@ use std::fmt::{Show,Formatter}; use std::os; use std::path::Path; use std::io::process::{Command,ProcessOutput,InheritFd}; -use util::{CargoResult,io_error,process_error}; +use util::{CargoResult, CargoError, ProcessError, process_error, box_error}; use std::collections::HashMap; #[deriving(Clone,PartialEq)] @@ -65,34 +65,36 @@ impl ProcessBuilder { } // TODO: should InheritFd be hardcoded? - pub fn exec(&self) -> CargoResult<()> { + pub fn exec(&self) -> Result<(), ProcessError> { let mut command = self.build_command(); command .env(self.build_env().as_slice()) .stdout(InheritFd(1)) .stderr(InheritFd(2)); - let exit = try!(command.status().map_err(io_error)); + let msg = || format!("Could not execute process `{}`", self.debug_string()); + + let exit = try!(command.status().map_err(|e| process_error(msg(), &command, None, None))); if exit.success() { Ok(()) } else { - let msg = format!("Could not execute process `{}`", self.debug_string()); - Err(process_error(msg, exit, None)) + Err(process_error(msg(), &command, Some(&exit), None)) } } - pub fn exec_with_output(&self) -> CargoResult { + pub fn exec_with_output(&self) -> Result { let mut command = self.build_command(); command.env(self.build_env().as_slice()); - let output = try!(command.output().map_err(io_error)); + let msg = || format!("Could not execute process `{}`", self.debug_string()); + + let output = try!(command.output().map_err(|e| process_error(msg(), &command, None, None))); if output.status.success() { Ok(output) } else { - let msg = format!("Could not execute process `{}`", self.debug_string()); - Err(process_error(msg, output.status.clone(), Some(output))) + Err(process_error(msg(), &command, Some(&output.status), Some(&output))) } } diff --git a/src/cargo/util/result.rs b/src/cargo/util/result.rs index 8d26fc1a5..8e0b7b563 100644 --- a/src/cargo/util/result.rs +++ b/src/cargo/util/result.rs @@ -1,232 +1,27 @@ -use std::fmt; -use std::fmt::{Show,Formatter}; -use std::io; -use std::io::IoError; -use std::io::process::{ProcessOutput,ProcessExit}; -use core::errors::{CLIError,CLIResult}; -use toml; - -/* - * CargoResult should be used in libcargo. CargoCliResult should be used in the - * various executables. - */ - -pub type CargoResult = Result; - -pub fn other_error(desc: &'static str) -> CargoError { - CargoError { - kind: OtherCargoError, - desc: StaticDescription(desc), - detail: None, - cause: None - } -} - -pub fn io_error(err: IoError) -> CargoError { - let desc = err.desc; - - CargoError { - kind: IoError(err), - desc: StaticDescription(desc), - detail: None, - cause: None - } -} - -pub fn process_error(detail: String, exit: ProcessExit, output: Option) -> CargoError { - CargoError { - kind: ProcessError(exit, output), - desc: BoxedDescription(detail), - detail: None, - cause: None - } -} - -pub fn human_error(desc: T, detail: U, cause: CargoError) -> CargoError { - CargoError { - kind: HumanReadableError, - desc: BoxedDescription(desc.to_str()), - detail: Some(detail.to_str()), - cause: Some(box cause) - } -} - -pub fn simple_human(desc: T) -> CargoError { - CargoError { - kind: HumanReadableError, - desc: BoxedDescription(desc.to_str()), - detail: None, - cause: None - } -} - -pub fn toml_error(desc: &'static str, error: toml::Error) -> CargoError { - CargoError { - kind: TomlError(error), - desc: StaticDescription(desc), - detail: None, - cause: None - } -} - -#[deriving(PartialEq,Clone)] -pub struct CargoError { - pub kind: CargoErrorKind, - desc: CargoErrorDescription, - detail: Option, - cause: Option> -} - -impl Show for CargoError { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self.desc { - StaticDescription(string) => try!(write!(f, "{}", string)), - BoxedDescription(ref string) => try!(write!(f, "{}", string)) - }; - - write!(f, "; kind={}", self.kind) - } -} - -#[deriving(PartialEq,Show,Clone)] -enum CargoErrorDescription { - StaticDescription(&'static str), - BoxedDescription(String) -} - -impl CargoError { - pub fn get_desc<'a>(&'a self) -> &'a str { - match self.desc { - StaticDescription(desc) => desc, - BoxedDescription(ref desc) => desc.as_slice() - } - } - - pub fn get_detail<'a>(&'a self) -> Option<&'a str> { - self.detail.as_ref().map(|s| s.as_slice()) - } - - pub fn with_detail(mut self, detail: T) -> CargoError { - self.detail = Some(detail.to_str()); - self - } - - pub fn to_cli(self, exit_code: uint) -> CLIError { - match self { - CargoError { kind: HumanReadableError, desc: BoxedDescription(desc), detail: detail, .. } => { - CLIError::new(desc, detail, exit_code) - }, - ref err @ CargoError { kind: InternalError, desc: StaticDescription(desc), detail: None, .. } => { - CLIError::new(format!("An unexpected error occurred: {}", err), Some(desc), exit_code) - }, - ref err @ CargoError { kind: InternalError, desc: StaticDescription(desc), detail: Some(ref detail), .. } => { - CLIError::new(format!("An unexpected error occurred: {}", err), Some(format!("{}\n{}", desc, detail)), exit_code) - }, - ref err @ _ => { - CLIError::new(format!("An unexpected error occurred: {}", err), None::<&str>, exit_code) - } - } - } -} - -#[deriving(PartialEq)] -pub enum CargoErrorKind { - HumanReadableError, - InternalError, - ProcessError(ProcessExit, Option), - IoError(io::IoError), - TomlError(toml::Error), - OtherCargoError -} - -impl Show for CargoErrorKind { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - &ProcessError(ref exit, _) => write!(f, "ProcessError({})", exit), - &HumanReadableError => write!(f, "HumanReadableError"), - &InternalError => write!(f, "InternalError"), - &IoError(ref err) => write!(f, "IoError({})", err), - &TomlError(ref err) => write!(f, "TomlError({})", err), - &OtherCargoError => write!(f, "OtherCargoError") - } - } -} - -impl Clone for CargoErrorKind { - fn clone(&self) -> CargoErrorKind { - match self { - &ProcessError(ref exit, ref output) => { - ProcessError(exit.clone(), output.as_ref().map(|output| ProcessOutput { - status: output.status.clone(), output: output.output.clone(), error: output.error.clone() - })) - }, - &HumanReadableError => HumanReadableError, - &InternalError => InternalError, - &IoError(ref err) => IoError(err.clone()), - &TomlError(ref err) => TomlError(err.clone()), - &OtherCargoError => OtherCargoError - } - } -} - -type CargoCliResult = Result; - -#[deriving(Show,Clone)] -pub struct CargoCliError { - kind: CargoCliErrorKind, - exit_status: uint, - desc: &'static str, - detail: Option, - cause: Option -} - -#[deriving(Show,Clone)] -pub enum CargoCliErrorKind { - OtherCargoCliError -} +use util::errors::{CargoResult, CargoError, chain}; pub trait Wrap { - fn wrap(self, desc: &'static str) -> Self; + fn wrap(self, error: E) -> Self; } -impl Wrap for Result { - fn wrap(self, desc: &'static str) -> Result { +impl Wrap for Result> { + fn wrap(self, error: E) -> CargoResult { match self { Ok(x) => Ok(x), - Err(e) => { - Err(CargoError { - kind: e.kind.clone(), - desc: StaticDescription(desc), - detail: None, - cause: Some(box e) - }) - } + Err(e) => Err(chain(e, error)) } } } pub trait Require { - fn require(self, err: CargoError) -> CargoResult; + fn require(self, err: || -> E) -> CargoResult; } impl Require for Option { - fn require(self, err: CargoError) -> CargoResult { + fn require(self, err: || -> E) -> CargoResult { match self { Some(x) => Ok(x), - None => Err(err) - } - } -} - -pub trait ToCLI { - fn to_cli(self, exit_code: uint) -> CLIResult; -} - -impl ToCLI for Result { - fn to_cli(self, exit_code: uint) -> CLIResult { - match self { - Ok(val) => Ok(val), - Err(err) => Err(err.to_cli(exit_code)) + None => Err(box err().concrete() as Box) } } } diff --git a/src/cargo/util/toml.rs b/src/cargo/util/toml.rs index 0bf40537f..261b9413b 100644 --- a/src/cargo/util/toml.rs +++ b/src/cargo/util/toml.rs @@ -7,14 +7,11 @@ use serialize::Decodable; use core::{SourceId,GitKind}; use core::manifest::{LibKind,Lib}; use core::{Summary,Manifest,Target,Dependency,PackageId}; -use util::{CargoResult,Require,simple_human,toml_error}; +use util::{CargoResult, Require, error, box_error}; pub fn to_manifest(contents: &[u8], source_id: &SourceId) -> CargoResult<(Manifest, Vec)> { - let root = try!(toml::parse_from_bytes(contents).map_err(|_| - simple_human("Cargo.toml is not valid Toml"))); - - let toml = try!(toml_to_manifest(root).map_err(|_| - simple_human("Cargo.toml is not a valid Cargo manifest"))); + let root = try!(toml::parse_from_bytes(contents).map_err(|_| error("Cargo.toml is not valid Toml"))); + let toml = try!(toml_to_manifest(root).map_err(|_| error("Cargo.toml is not a valid manifest"))); toml.to_manifest(source_id) } @@ -28,7 +25,7 @@ fn toml_to_manifest(root: toml::Value) -> CargoResult { toml::from_toml(root.clone()) } - let project = try!(decode(&root, "project").map_err(|e| toml_error("ZOMG", e))); + let project = try!(decode(&root, "project").map_err(box_error)); let lib = decode(&root, "lib").ok(); let bin = decode(&root, "bin").ok(); @@ -36,7 +33,7 @@ fn toml_to_manifest(root: toml::Value) -> CargoResult { let deps = match deps { Some(deps) => { - let table = try!(deps.get_table().require(simple_human("dependencies must be a table"))).clone(); + let table = try!(deps.get_table().require(|| "dependencies must be a table")).clone(); let mut deps: HashMap = HashMap::new(); @@ -48,13 +45,13 @@ fn toml_to_manifest(root: toml::Value) -> CargoResult { for (k, v) in table.iter() { let v = try!(v.get_str() - .require(simple_human("dependency values must be string"))); + .require(|| "dependency values must be string")); details.insert(k.clone(), v.clone()); } let version = try!(details.find_equiv(&"version") - .require(simple_human("dependencies must include a version"))).clone(); + .require(|| "dependencies must include a version")).clone(); deps.insert(k.clone(), DetailedDep(DetailedTomlDependency { version: version, diff --git a/tests/support/mod.rs b/tests/support/mod.rs index a7cde6a65..552852a3d 100644 --- a/tests/support/mod.rs +++ b/tests/support/mod.rs @@ -11,7 +11,7 @@ use std::fmt::Show; use ham = hamcrest; use cargo::core::shell; use cargo::util::{process,ProcessBuilder,CargoError}; -use cargo::util::result::ProcessError; +use cargo::util::ProcessError; pub mod paths; @@ -253,7 +253,7 @@ impl ham::Matcher for Execs { match res { Ok(out) => self.match_output(&out), - Err(CargoError { kind: ProcessError(_, ref out), .. }) => self.match_output(out.get_ref()), + Err(ProcessError { output: Some(ref out), .. }) => self.match_output(out), Err(e) => Err(format!("could not exec process {}: {}", process, e)) } } diff --git a/tests/test_cargo_compile.rs b/tests/test_cargo_compile.rs index 39cc57026..52635b4d3 100644 --- a/tests/test_cargo_compile.rs +++ b/tests/test_cargo_compile.rs @@ -42,7 +42,7 @@ test!(cargo_compile_with_invalid_manifest { assert_that(p.cargo_process("cargo-compile"), execs() .with_status(101) - .with_stderr("Cargo.toml is not a valid Cargo manifest")); + .with_stderr("Cargo.toml is not a valid manifest")); }) test!(cargo_compile_without_manifest { @@ -64,7 +64,7 @@ test!(cargo_compile_with_invalid_code { assert_that(p.cargo_process("cargo-compile"), execs() .with_status(101) - .with_stderr(format!("src/foo.rs:1:1: 1:8 error: expected item but found `invalid`\nsrc/foo.rs:1 invalid rust code!\n ^~~~~~~\nfailed to execute: `rustc src/foo.rs --crate-type bin --out-dir {} -L {}`", target.display(), target.join("deps").display()).as_slice())); + .with_stderr(format!("src/foo.rs:1:1: 1:8 error: expected item but found `invalid`\nsrc/foo.rs:1 invalid rust code!\n ^~~~~~~\nExecuting `rustc 'src/foo.rs' '--crate-type' 'bin' '--out-dir' '{}' '-L' '{}'` failed (status=101)", target.display(), target.join("deps").display()).as_slice())); }) test!(cargo_compile_with_warnings_in_the_root_package { diff --git a/tests/test_cargo_compile_git_deps.rs b/tests/test_cargo_compile_git_deps.rs index 94bcbede1..117b7edc7 100644 --- a/tests/test_cargo_compile_git_deps.rs +++ b/tests/test_cargo_compile_git_deps.rs @@ -1,12 +1,25 @@ -use support::{ProjectBuilder,ResultTest,project,execs,main_file}; +use std::io::File; + +use support::{ProjectBuilder, ResultTest, project, execs, main_file, paths}; use hamcrest::{assert_that,existing_file}; use cargo; -use cargo::util::{CargoResult,process}; +use cargo::util::{CargoResult, ProcessError, process}; fn setup() { } -fn git_repo(name: &str, callback: |ProjectBuilder| -> ProjectBuilder) -> CargoResult { +fn git_repo(name: &str, callback: |ProjectBuilder| -> ProjectBuilder) -> Result { + let gitconfig = paths::home().join(".gitconfig"); + + if !gitconfig.exists() { + File::create(&gitconfig).write(r" + [user] + + email = foo@bar.com + name = Foo Bar + ".as_bytes()).assert() + } + let mut git_project = project(name); git_project = callback(git_project); git_project.build(); -- 2.30.2